iOS アプリに Flutter 画面を追加する
このガイドでは、既存の iOS アプリに単一の Flutter 画面を追加する方法について説明します。
FlutterEngineとFlutterViewControllerを起動する
既存の iOS から Flutter 画面を起動するには、FlutterEngine
そしてFlutterViewController
。
のFlutterEngine
あなたと同じ寿命かもしれないFlutterViewController
あるいはあなたの長生きをするFlutterViewController
。
見るロードシーケンスとパフォーマンスレイテンシとメモリに関する詳細な分析については、 エンジンの予熱のトレードオフ。
FlutterEngineを作成する
を作成する場所FlutterEngine
ホストアプリによって異なります。
この例では、FlutterEngine
SwiftUI 内のオブジェクトObservableObject
。
次にこれを渡しますFlutterEngine
にContentView
を使用してenvironmentObject()
財産。
import SwiftUI
import Flutter
// The following library connects plugins with iOS platform code to this app.
import FlutterPluginRegistrant
class FlutterDependencies: ObservableObject {
let flutterEngine = FlutterEngine(name: "my flutter engine")
init(){
// Runs the default Dart entrypoint with a default Flutter route.
flutterEngine.run()
// Connects plugins with iOS platform code to this app.
GeneratedPluginRegistrant.register(with: self.flutterEngine);
}
}
@main
struct MyApp: App {
// flutterDependencies will be injected using EnvironmentObject.
@StateObject var flutterDependencies = FlutterDependencies()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(flutterDependencies)
}
}
}
例として、FlutterEngine
、アプリの起動時にプロパティとして公開されます
アプリのデリゲート。
import UIKit
import Flutter
// The following library connects plugins with iOS platform code to this app.
import FlutterPluginRegistrant
@UIApplicationMain
class AppDelegate: FlutterAppDelegate { // More on the FlutterAppDelegate.
lazy var flutterEngine = FlutterEngine(name: "my flutter engine")
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Runs the default Dart entrypoint with a default Flutter route.
flutterEngine.run();
// Connects plugins with iOS platform code to this app.
GeneratedPluginRegistrant.register(with: self.flutterEngine);
return super.application(application, didFinishLaunchingWithOptions: launchOptions);
}
}
この例では、FlutterEngine
SwiftUI 内のオブジェクトObservableObject
。
次にこれを渡しますFlutterEngine
にContentView
を使用してenvironmentObject()
財産。
@import UIKit;
@import Flutter;
@interface AppDelegate : FlutterAppDelegate // More on the FlutterAppDelegate below.
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
// The following library connects plugins with iOS platform code to this app.
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
// Runs the default Dart entrypoint with a default Flutter route.
[self.flutterEngine run];
// Connects plugins with iOS platform code to this app.
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
FlutterEngine で FlutterViewController を表示する
次の例は、一般的なContentView
とともにButton
プレゼントに夢中FlutterViewController
。
のFlutterViewController
コンストラクターは事前にウォームアップされたものを受け取りますFlutterEngine
議論として。FlutterEngine
渡される
としてEnvironmentObject
経由flutterDependencies
。
import SwiftUI
import Flutter
struct ContentView: View {
// Flutter dependencies are passed in an EnvironmentObject.
@EnvironmentObject var flutterDependencies: FlutterDependencies
// Button is created to call the showFlutter function when pressed.
var body: some View {
Button("Show Flutter!") {
showFlutter()
}
}
func showFlutter() {
// Get the RootViewController.
guard
let windowScene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
let window = windowScene.windows.first(where: \.isKeyWindow),
let rootViewController = window.rootViewController
else { return }
// Create the FlutterViewController.
let flutterViewController = FlutterViewController(
engine: flutterDependencies.flutterEngine,
nibName: nil,
bundle: nil)
flutterViewController.modalPresentationStyle = .overCurrentContext
flutterViewController.isViewOpaque = false
rootViewController.present(flutterViewController, animated: true)
}
}
次の例は、一般的なViewController
とともにUIButton
プレゼントに夢中FlutterViewController
。
のFlutterViewController
を使用しますFlutterEngine
実例
で作成されたAppDelegate
。
import UIKit
import Flutter
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Make a button to call the showFlutter function when pressed.
let button = UIButton(type:UIButton.ButtonType.custom)
button.addTarget(self, action: #selector(showFlutter), for: .touchUpInside)
button.setTitle("Show Flutter!", for: UIControl.State.normal)
button.frame = CGRect(x: 80.0, y: 210.0, width: 160.0, height: 40.0)
button.backgroundColor = UIColor.blue
self.view.addSubview(button)
}
@objc func showFlutter() {
let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
let flutterViewController =
FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
present(flutterViewController, animated: true, completion: nil)
}
}
次の例は、一般的なViewController
とともにUIButton
プレゼントに夢中FlutterViewController
。
のFlutterViewController
を使用しますFlutterEngine
実例
で作成されたAppDelegate
。
@import Flutter;
#import "AppDelegate.h"
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Make a button to call the showFlutter function when pressed.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self
action:@selector(showFlutter)
forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"Show Flutter!" forState:UIControlStateNormal];
button.backgroundColor = UIColor.blueColor;
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[self.view addSubview:button];
}
- (void)showFlutter {
FlutterEngine *flutterEngine =
((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
FlutterViewController *flutterViewController =
[[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
[self presentViewController:flutterViewController animated:YES completion:nil];
}
@end
これで、iOS アプリに Flutter 画面が埋め込まれました。
あるいは- 暗黙的な FlutterEngine を使用して FlutterViewController を作成する
前の例の代わりに、次のようにすることもできます。FlutterViewController
暗黙的に独自のものを作成するFlutterEngine
それなし
事前に予熱を行ってください。
これは通常は推奨されません。FlutterEngine
オンデマンドは顕著な問題を引き起こす可能性があります
までの待ち時間FlutterViewController
は
表示され、最初のフレームをレンダリングするとき。ただし、これは次の可能性があります
Flutter 画面がめったに表示されない場合や、適切な機能がない場合に便利です。
Dart VM をいつ開始する必要があるか、および Flutter をいつ開始するかを決定するためのヒューリスティック
ビューコントローラー間で状態を保持する必要はありません。
させるにはFlutterViewController
存在せずに存在するFlutterEngine
を省略します。FlutterEngine
を構築し、FlutterViewController
エンジンリファレンスなし。
import SwiftUI
import Flutter
struct ContentView: View {
var body: some View {
Button("Show Flutter!") {
openFlutterApp()
}
}
func openFlutterApp() {
// Get the RootViewController.
guard
let windowScene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
let window = windowScene.windows.first(where: \.isKeyWindow),
let rootViewController = window.rootViewController
else { return }
// Create the FlutterViewController without an existing FlutterEngine.
let flutterViewController = FlutterViewController(
project: nil,
nibName: nil,
bundle: nil)
flutterViewController.modalPresentationStyle = .overCurrentContext
flutterViewController.isViewOpaque = false
rootViewController.present(flutterViewController, animated: true)
}
}
// Existing code omitted.
func showFlutter() {
let flutterViewController = FlutterViewController(project: nil, nibName: nil, bundle: nil)
present(flutterViewController, animated: true, completion: nil)
}
// Existing code omitted.
- (void)showFlutter {
FlutterViewController *flutterViewController =
[[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
[self presentViewController:flutterViewController animated:YES completion:nil];
}
@end
見るロードシーケンスとパフォーマンスレイテンシとメモリ使用量について詳しく調べてください。
FlutterAppDelegate の使用
アプリケーションのUIApplicationDelegate
サブクラスFlutterAppDelegate
推奨されていますが、必須ではありません。
のFlutterAppDelegate
次のような機能を実行します。
- 次のようなアプリケーション コールバックの転送
openURL
などのプラグインにローカル認証。 - Flutter接続を開いたままにする 電話画面がロックされているときはデバッグモードで。
FlutterAppDelegate サブクラスの作成
のサブクラスの作成FlutterAppDelegate
UIKitアプリで表示されました
の中にFlutterEngine と FlutterViewController セクションを開始する。
SwiftUI アプリでは、FlutterAppDelegate
に準拠するものObservableObject
次のようなプロトコル:
import SwiftUI
import Flutter
import FlutterPluginRegistrant
class AppDelegate: FlutterAppDelegate, ObservableObject {
let flutterEngine = FlutterEngine(name: "my flutter engine")
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Runs the default Dart entrypoint with a default Flutter route.
flutterEngine.run();
// Used to connect plugins (only if you have plugins with iOS platform code).
GeneratedPluginRegistrant.register(with: self.flutterEngine);
return true;
}
}
@main
struct MyApp: App {
// Use this property wrapper to tell SwiftUI
// it should use the AppDelegate class for the application delegate
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
次に、あなたの見解では、AppDelegate
としてアクセス可能ですEnvironmentObject
。
import SwiftUI
import Flutter
struct ContentView: View {
// Access the AppDelegate using an EnvironmentObject.
@EnvironmentObject var appDelegate: AppDelegate
var body: some View {
Button("Show Flutter!") {
openFlutterApp()
}
}
func openFlutterApp() {
// Get the RootViewController.
guard
let windowScene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
let window = windowScene.windows.first(where: \.isKeyWindow),
let rootViewController = window.rootViewController
else { return }
// Create the FlutterViewController.
let flutterViewController = FlutterViewController(
// Access the Flutter Engine via AppDelegate.
engine: appDelegate.flutterEngine,
nibName: nil,
bundle: nil)
flutterViewController.modalPresentationStyle = .overCurrentContext
flutterViewController.isViewOpaque = false
rootViewController.present(flutterViewController, animated: true)
}
}
FlutterAppDelegateを直接サブクラスにできない場合
アプリのデリゲートが直接作成できない場合は、FlutterAppDelegate
サブクラス、
アプリのデリゲートにFlutterAppLifeCycleProvider
プラグインが必要なコールバックを確実に受信できるようにするためのプロトコルです。
そうしないと、これらのイベントに依存するプラグインの動作が未定義になる可能性があります。
例えば:
import Foundation
import Flutter
class AppDelegate: UIResponder, UIApplicationDelegate, FlutterAppLifeCycleProvider, ObservableObject {
private let lifecycleDelegate = FlutterPluginAppLifeCycleDelegate()
let flutterEngine = FlutterEngine(name: "flutter_nps_engine")
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
flutterEngine.run()
return lifecycleDelegate.application(application, didFinishLaunchingWithOptions: launchOptions ?? [:])
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
lifecycleDelegate.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
lifecycleDelegate.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
lifecycleDelegate.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return lifecycleDelegate.application(app, open: url, options: options)
}
func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
return lifecycleDelegate.application(application, handleOpen: url)
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
return lifecycleDelegate.application(application, open: url, sourceApplication: sourceApplication ?? "", annotation: annotation)
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
lifecycleDelegate.application(application, performActionFor: shortcutItem, completionHandler: completionHandler)
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
lifecycleDelegate.application(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
lifecycleDelegate.application(application, performFetchWithCompletionHandler: completionHandler)
}
func add(_ delegate: FlutterApplicationLifeCycleDelegate) {
lifecycleDelegate.add(delegate)
}
}
@import Flutter;
@import UIKit;
@import FlutterPluginRegistrant;
@interface AppDelegate : UIResponder <UIApplicationDelegate, FlutterAppLifeCycleProvider>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
実装は主に a に委任する必要があります。FlutterPluginAppLifeCycleDelegate
:
@interface AppDelegate ()
@property (nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
@end
@implementation AppDelegate
- (instancetype)init {
if (self = [super init]) {
_lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
}
return self;
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id>*))launchOptions {
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
[self.flutterEngine runWithEntrypoint:nil];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}
// Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController {
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([viewController isKindOfClass:[FlutterViewController class]]) {
return (FlutterViewController*)viewController;
}
return nil;
}
- (void)application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
[_lifeCycleDelegate application:application
didRegisterUserNotificationSettings:notificationSettings];
}
- (void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
[_lifeCycleDelegate application:application
didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[_lifeCycleDelegate application:application
didReceiveRemoteNotification:userInfo
fetchCompletionHandler:completionHandler];
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
return [_lifeCycleDelegate application:application openURL:url options:options];
}
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
return [_lifeCycleDelegate application:application handleOpenURL:url];
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
sourceApplication:(NSString*)sourceApplication
annotation:(id)annotation {
return [_lifeCycleDelegate application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
- (void)application:(UIApplication*)application
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler {
[_lifeCycleDelegate application:application
performActionForShortcutItem:shortcutItem
completionHandler:completionHandler];
}
- (void)application:(UIApplication*)application
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
completionHandler:(nonnull void (^)(void))completionHandler {
[_lifeCycleDelegate application:application
handleEventsForBackgroundURLSession:identifier
completionHandler:completionHandler];
}
- (void)application:(UIApplication*)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[_lifeCycleDelegate application:application performFetchWithCompletionHandler:completionHandler];
}
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate {
[_lifeCycleDelegate addDelegate:delegate];
}
@end
起動オプション
この例では、デフォルトの起動設定を使用して Flutter を実行する方法を示します。
Flutter ランタイムをカスタマイズするには、 Dart エントリポイント、ライブラリ、ルートを指定することもできます。
ダーツエントリーポイント
電話をかけるrun
にFlutterEngine
、デフォルトでは、
を実行しますmain()
ダーツ機能
あなたのlib/main.dart
ファイル。
次を使用して、別のエントリポイント関数を実行することもできます。runWithEntrypoint
とNSString
指定する
別の Dart 機能。
ダーツライブラリー
Dart 関数の指定に加えて、エントリポイントも指定できます。 特定のファイル内の関数。
たとえば、次の実行myOtherEntrypoint()
のlib/other_file.dart
それ以外のmain()
のlib/main.dart
:
flutterEngine.run(withEntrypoint: "myOtherEntrypoint", libraryURI: "other_file.dart")
[flutterEngine runWithEntrypoint:@"myOtherEntrypoint" libraryURI:@"other_file.dart"];
ルート
Flutter バージョン 1.22 以降、Flutter の初期ルートを設定できるようになりましたWidgetsApp
FlutterEngine または
FlutterViewController。
let flutterEngine = FlutterEngine()
// FlutterDefaultDartEntrypoint is the same as nil, which will run main().
engine.run(
withEntrypoint: "main", initialRoute: "/onboarding")
FlutterEngine *flutterEngine = [[FlutterEngine alloc] init];
// FlutterDefaultDartEntrypoint is the same as nil, which will run main().
[flutterEngine runWithEntrypoint:FlutterDefaultDartEntrypoint
initialRoute:@"/onboarding"];
このコードは、dart:ui
のwindow.defaultRouteName
に"/onboarding"
それ以外の"/"
。
あるいは、事前ウォーミングを行わずに FlutterViewController を直接構築することもできます。 flutterエンジン:
let flutterViewController = FlutterViewController(
project: nil, initialRoute: "/onboarding", nibName: nil, bundle: nil)
FlutterViewController* flutterViewController =
[[FlutterViewController alloc] initWithProject:nil
initialRoute:@"/onboarding"
nibName:nil
bundle:nil];
見るナビゲーションとルーティングFlutter のルートについて詳しくは、こちらをご覧ください。
他の
前の例は、カスタマイズする方法をいくつか示しているだけです
Flutter インスタンスがどのように開始されるか。使用するプラットフォームチャネル、
データをプッシュしたり、Flutter 環境を準備したりするのは自由です
Flutter UI を表示する前に、任意の方法で、FlutterViewController
。